import os, os.path, shutil, time, datetime
from django.http import HttpResponseRedirect
from django.contrib.auth.models import User
from django.db import models
from market.math import sumVectorElements
from market.marketData import MarketData
from market.playerData import PlayerData
from market.models import UserProfile, Asset, Player
from market.models import Strategy, AssetPercentageInStrategy, Fund, WebSiteComment
from market.models import History, AssetForHistory, StrategyForHistory, PlayerPortfolioForHistory
from market.models import DigitalContract, BidOnContract, OfferOnContract, LiquidityToken, TradedContract, LastVisitedContract
from django.utils import simplejson
from market.math import isARepartition


##################################################################################################
#get oject from a txt file path
def getObjectFromFile( pathAndFile ):
    try:
        stratFile = open(pathAndFile, 'r')
    except:
        raise Exception( pathAndFile + ' is  not valid' )
    try:
        return simplejson.loads( stratFile.read() )
    except:
        raise Exception( pathAndFile + ' cannot be loaded as a json' )
    
    
    
##################################################################################################
#get oject from a txt file path
def getObjectFromPathAndFile( path0, betroomName, fileNameDotTxt ):
    if path0 == '':
        mypath = os.getcwd()
    else:
        mypath = path0
        
    mypath1 = os.path.join( mypath, betroomName )
    filename = os.path.join( mypath1, fileNameDotTxt )
    if (not os.path.isfile(filename)):
        mypath1 = os.path.join( mypath , 'savedRoomFiles', betroomName )
        filename = os.path.join( mypath1, fileNameDotTxt )
    if (not os.path.isfile(filename)) :
        raise Exception('getObjectFromPathAndFile: Folder '+ mypath1+ ' must contain ' +fileNameDotTxt )
    return ( getObjectFromFile( filename ), mypath1 )
    

##################################################################################################
#does not throw if all the strings in the string list are in the dictionary
def checkDictionary( dictionary, entryList ):
    for e in entryList:
        if not( e in dictionary ):
            raise Exception( e + '  is not in the dictionary. Possible values: '+str(dictionary))

##################################################################################################
#check that we only invest listed strategies and valid in the sens that thei are a view on regular assets
# or a single non regular asset with vala fix value 
def checkThatPlayerInvesTOnlyDefinedStrat( investedStratNameList, allStratDescription, playerName):
    if len( investedStratNameList ) < 1:
        raise 'no strategy in the portfollio'
    for s in investedStratNameList:
        if not( s in allStratDescription ):
            raise Exception('Player %s invest strategy %s which is not listed' %(playerName,s) )


##################################################################################################
# add the Assets to the base (merges the asset listed in the .txt with the asset already in the data base
# then remove the assets not listed in the .txt)
def addAssetsToBase(unAdjustedPath,betroom):
    # get the assets from the file
    assetsAndAdjustedPath = getObjectFromPathAndFile( unAdjustedPath, betroom.name, 'Assets.txt' )
    assetObjects = assetsAndAdjustedPath[0]
    checkDictionary( assetObjects, ['assets'] )
    assetEntries = ['isAvailableForStandardStrategy', 'valueMultiplier', 'fixValue', 'isStandard', 'name']
    for i, v in enumerate(assetObjects['assets']): 
        checkDictionary( v, assetEntries )

    # create assets
    assetNameList =[]
    for i, dictionaryForAsset in enumerate(assetObjects['assets']):
        nameTmp = dictionaryForAsset['name']
        assetNameList.append( nameTmp )
        la = Asset.objects.filter( name = nameTmp )
        if len(la) == 0:
            a = Asset(
                name = nameTmp,
                isAvailableForStandardStrategy = dictionaryForAsset['isAvailableForStandardStrategy'],
                valueMultiplier = dictionaryForAsset['valueMultiplier'],
                fixValue = dictionaryForAsset['fixValue'],
                isStandard = dictionaryForAsset['isStandard'],
                betroom = betroom,
                description = dictionaryForAsset['description']
            )
            a.save()
        else:
            if( la[0].betroom != betroom ):
                raise Exception( "%s already exists in another betroom" %(la[0].name) )
            la[0].isAvailableForStandardStrategy = dictionaryForAsset['isAvailableForStandardStrategy']
            la[0].valueMultiplier = dictionaryForAsset['valueMultiplier']
            la[0].fixValue = dictionaryForAsset['fixValue']
            la[0].isStandard = dictionaryForAsset['isStandard']
            la[0].description = dictionaryForAsset['description']
            la[0].save()
            
    #remove old assets
    for a in Asset.objects.filter( betroom = betroom ):
        if not( a.name in assetNameList ):
            a.delete()

    # return the path to the file
    return (assetsAndAdjustedPath[1], assetObjects )

##################################################################################################
# chech the validity of a set of assets : either a list of standards assets or a single non std asset with fixed value
# we register all the codes of the asset we find. -1 : invalid asset, 1: std, 2: non std valid
# at the end the list of the code must have the same siz  that the list of the asset.
# If it is the case the strategy is valid if sum( code ) == lengh or if lengh==1 and code == 2
def checkStrategy( assetList, allAssetDescription ):
    correctLen = len( assetList )
    if correctLen < 1:
        raise 'checkStrategy: no asset in the strategy'

    codeOfAsset = []
    for i, v in enumerate(allAssetDescription):
        if v['name'] in assetList:
            tmp0 = -1
            if v['isAvailableForStandardStrategy']:
                tmp0 = 1
            else:
                if v['fixValue'] > 0:
                    tmp0 = 2
            codeOfAsset.append( tmp0 )
    # look the codeOfAsset string to evaluate the return
    if len( codeOfAsset ) != correctLen:
        raise Exception('checkStrategy: some assets are not listed: %s' %str([a['name'] for a in allAssetDescription]) + " VS " + str(assetList))
    if sumVectorElements( codeOfAsset ) == correctLen:
        return
    if correctLen == 1 and codeOfAsset[ 0 ] == 2:
        return
    raise 'checkStrategy: strange error'

##################################################################################################
# add the Strategies to the base (merges the asset listed in the .txt with the strategies already in the data base
# then remove the strategies not listed in the .txt)
# it also return the adjusted path to the Assets.txt file
def addStrategiesToBase( path, betroom, assetObjects ):
    # get the strategies from the file
    stratObjects = getObjectFromFile( os.path.join( path,'Strategies.txt') ) 
    checkDictionary( stratObjects, ['strategies'] )
    stratEntries = ['name', 'assetInStrat']
    # check the strategies
    #asset use for tests
    allStratNames =[]
    for i, v in enumerate(stratObjects['strategies']): 
        checkDictionary( v, stratEntries )
        isRep = isARepartition( [rep['assetPerc'] / 100 for rep in (v['assetInStrat']) ] )
        if not( isRep[ 0 ] ):
	    raise Exception( v['name'] +" is not a repartition: " + str( isRep[ 1 ] ) )
        checkStrategy( [rep['assetName'] for rep in (v['assetInStrat']) ], assetObjects['assets'] )
        allStratNames.append( v['name'] )

    stratNameList =[]
    for i, dictionaryForStrat in enumerate(stratObjects['strategies']):
        nameTmp = dictionaryForStrat['name']
        stratNameList.append( nameTmp )
        ls = Strategy.objects.filter( name = nameTmp )
        if len(ls) != 0:
            if( ls[0].betroom != betroom ):
                raise Exception( "%s already exists in another betroom" %(ls[0].name) )
            s = ls[ 0 ]
            for ais in AssetPercentageInStrategy.objects.filter( strategy = s ):
                ais.delete()
        else:
            s = Strategy( name = dictionaryForStrat['name'], betroom = betroom )
        s.save()        

        for i, dictionaryForAsset in enumerate(dictionaryForStrat['assetInStrat']):
            apis = AssetPercentageInStrategy(
                asset = Asset.objects.get( name = dictionaryForAsset['assetName'] ),
                strategy = s,
                assetPercentageInStrategy = dictionaryForAsset['assetPerc'] / 100 )
            apis.save()
            
    #remove old strategies
    for s in Strategy.objects.filter( betroom = betroom ):
        if not( s.name in stratNameList ):
            s.delete()

    return allStratNames

##################################################################################################
# add the Player from the *player.txt files
def addPlayersToBase(adjustedPath,betroom, allStratNames):

    # check the player.txt files 
    allFilesList = os.listdir(adjustedPath)
    playerObjects = []
    playerEntries = ['fundInPortfolio', 'mtm', 'mainStratName', 'name']
    for f in allFilesList:
        if f[-10:] == 'player.txt':
            tmp = getObjectFromFile( os.path.join( adjustedPath,f) )
            checkDictionary( tmp, playerEntries )
            playerName = tmp['name']

            #check that we only invest listed strategies and valid in the sens that thei are a view on regular assets
            # or a single non regular asset with vala fix value
            tmp2 = [strat['stratName'] for strat in (tmp['fundInPortfolio']) ]
            if not( tmp['mainStratName'] in tmp2 ):
                raise Exception('player '+  playerName +' is not invested in his own main stategy' )
            checkThatPlayerInvesTOnlyDefinedStrat( tmp2, allStratNames, playerName )
            # check that the percentages in different strat forms a repartition
            isRep = isARepartition( [strat['fundMtMSetPerc'] / 100 for strat in (tmp['fundInPortfolio']) ] )
            if not( isRep[0] ):
                raise  "%s has an unvalid mtm repartition: %s" %(tmp['name']),isRep[1] 
            playerObjects.append( tmp )
            
    # create players and portfolio
    allPlayerList=[]
    playerNameList =[]
    allPlayerCurrentRep=[]
    for dictionaryForPlayer in playerObjects:
        nameTmp = dictionaryForPlayer['name']
        playerNameList.append( nameTmp )
        allPlayerCurrentRep.append( (nameTmp,[]) )
        # get the user. if he does not exist, create one
        try:
            user = User.objects.get(username = nameTmp)
        except User.DoesNotExist:
            user = User(username = nameTmp )
            user.save()
        user.get_profile().nameOfTheLastVistedBetRoom = betroom.name
        user.get_profile().save()
        
        # if the player exists, we delet it properly
        try:
            p = Player.objects.get( userBeingThisPlayer = user, mainStrategy__betroom = betroom )
            for nis in Fund.objects.filter( player = p ):
                nis.delete()
            p.delete()
        except Player.DoesNotExist:
            # do nothing, it is fine is the player does not exist
            pass
        
        # ceate and save a new player
        p = Player(
            userBeingThisPlayer = user,
            mainStrategy = Strategy.objects.get( name = dictionaryForPlayer['mainStratName'] )
            )
        p.save()
        for i, dictionaryForFund in enumerate(dictionaryForPlayer['fundInPortfolio']):
            fund = Fund(
                strategy = Strategy.objects.get( name = dictionaryForFund['stratName'] ),
                player = p,
                fundTargetPercentageInPortfolio = dictionaryForFund['fundMtMSetPerc'] / 100
                )
            fund.save()
            allPlayerCurrentRep[len(allPlayerCurrentRep)-1][1].append( (fund,dictionaryForFund['fundMtMCurrentPerc'] / 100) )
        pp = PlayerData( user, betroom )
        allPlayerList.append([pp,dictionaryForPlayer['mtm']])
        
    #remove old players
    for p in Player.objects.filter( mainStrategy__betroom = betroom ):
        if not( p.userBeingThisPlayer.username in playerNameList ):
            p.delete()

    #now that all the main strategie have been defined. The market can be created.
    m = MarketData(betroom)
    # with the specified market, the MTM of the fund and the strat of the fund, the numbers of asset can be computed
    for pp in allPlayerList:
        index = playerNameList.index(pp[0].getName())
        pp[0].setNumberOfAssetPerStrategy( pp[1], allPlayerCurrentRep[index][1], m )



##################################################################################################     
# not really core: add the betroom information to the data base
def addBetroomInfoToBase(path,betroom):  
    infosObjects = getObjectFromFile( os.path.join( path, 'betroom.txt') )
    checkDictionary( infosObjects, ['name', 'personInCharge', 'description', 'status', 'password'] )
    betroom.name = infosObjects['name']
    betroom.description = infosObjects['description']
    betroom.status = infosObjects['status']
    betroom.password = infosObjects['password']
    betroom.personInCharge = User.objects.get(username = infosObjects['personInCharge'])
    betroom.save()


################################################################################################## 
def getAsset0( name ):
    try:
        return Asset.objects.get( name = name )
    except:
        raise Exception( name + ' not found among ' + str([a.name for a in Asset.objects.all()]) )
                   
##################################################################################################     
# not really core: add the history
def addHistoryToBase(path, betroom):

    # erase all existing histories
    History.objects.filter( betroom = betroom ).delete()
    
    historyObjects = getObjectFromFile( os.path.join( path, 'history.txt') )
    checkDictionary( historyObjects, ['history'] )
    historyEntries = ['time', 'bettingPlayerName', 'actionTaken', 'assets', 'strategies', 'playerPortfolios']
    for i, dictionaryForHistory in enumerate(historyObjects['history']):
        checkDictionary( dictionaryForHistory, historyEntries )
        strp_time = time.strptime( dictionaryForHistory['time'], "%d/%m/%Y %H:%M:%S")
        historyDate = datetime.datetime.fromtimestamp(time.mktime(strp_time))
        h = History(
                betroom = betroom,
                time  = historyDate,
                bettingPlayerName = dictionaryForHistory['bettingPlayerName'],
                actionTaken = dictionaryForHistory['actionTaken']
                )
        h.save()
        for assetInHisto in dictionaryForHistory['assets']:
            checkDictionary( assetInHisto, ['assetName', 'assetPrice'] )
            a = AssetForHistory(
                history = h,
                asset = getAsset0( assetInHisto['assetName'] ),
                price = assetInHisto['assetPrice'] 
            )
            a.save()
        for stratInHisto in dictionaryForHistory['strategies']:
            checkDictionary( stratInHisto, ['strategyName', 'strategyMtM', 'strategyPerfSinceLastHistoryDate',
                                                'numberOfPlayersInvestedInTheStrategy', 'totalNumberOfAssetInvestedInTheStrategy'] )
            s = StrategyForHistory(
                history = h,
                strategy = Strategy.objects.get( name = stratInHisto['strategyName'] ),
                strategyMtM = stratInHisto['strategyMtM'],
                strategyPerfSinceLastHistoryDate = stratInHisto['strategyPerfSinceLastHistoryDate'],
                numberOfPlayersInvestedInTheStrategy = stratInHisto['numberOfPlayersInvestedInTheStrategy'],
                totalNumberOfAssetInvestedInTheStrategy = stratInHisto['totalNumberOfAssetInvestedInTheStrategy']
            )
            s.save()
        for portfolioInHisto in dictionaryForHistory['playerPortfolios']:
            checkDictionary( portfolioInHisto, ['playerName', 'portfolioMtM', 'investementInMainStrategyPercentage', 'numberOfStrategies'] )
            p = PlayerPortfolioForHistory(
                    history = h,
                    player = Player.objects.get( userBeingThisPlayer__username = portfolioInHisto['playerName'], mainStrategy__betroom = betroom ),
                    portfolioMtM = portfolioInHisto['portfolioMtM'],
                    investementInMainStrategyPercentage = portfolioInHisto['investementInMainStrategyPercentage'],
                    numberOfStrategies = portfolioInHisto['numberOfStrategies']
                )
            p.save()            

##################################################################################################
# add the contracts
def addContracts(path,betroom):

    contractObjects = getObjectFromFile( os.path.join( path,'Contracts.txt') ) 
    checkDictionary( contractObjects, ['contracts'] )
    contractEntries = ['description', 'expiry', 'settlementStatus', 'issuer', 'name']
    for i, v in enumerate(contractObjects['contracts']): 
        checkDictionary( v, contractEntries )

    # erase all existing contract
    DigitalContract.objects.filter( issuer__mainStrategy__betroom = betroom ).delete()
    
    # create contracts
    for i, dictionaryForContract in enumerate(contractObjects['contracts']):
        strp_time = time.strptime( dictionaryForContract['expiry'], "%d/%m/%Y %H:%M:%S")
        expiry = datetime.datetime.fromtimestamp(time.mktime(strp_time))
        c = DigitalContract(
                issuer = Player.objects.get( userBeingThisPlayer__username = dictionaryForContract['issuer'], mainStrategy__betroom = betroom ),
                name = dictionaryForContract['name'],
                description = dictionaryForContract['description'],
                expiry = expiry,
                settlementStatus = dictionaryForContract['settlementStatus']
            )
        c.save()
    return 0

##################################################################################################
# add the contracts
def addTradedContracts(path,betroom):

    tradedContractObjects = getObjectFromFile( os.path.join( path, 'TradedContracts.txt') )
    checkDictionary( tradedContractObjects, ['tradedContracts'] )
    tradedContractEntries = ['buyer', 'name', 'seller', 'tradeDate', 'tradedPrice']
    for i, v in enumerate( tradedContractObjects['tradedContracts'] ): 
        checkDictionary( v, tradedContractEntries )

    # erase all existing traded contracts
    TradedContract.objects.filter( contract__issuer__mainStrategy__betroom = betroom ).delete()
            
    # create traded contract
    for i, dictionaryForTradedContract in enumerate(tradedContractObjects['tradedContracts']):
        strp_time = time.strptime( dictionaryForTradedContract['tradeDate'], "%d/%m/%Y %H:%M:%S")
        tradeDate = datetime.datetime.fromtimestamp(time.mktime(strp_time))
        tc = TradedContract(
                contract = DigitalContract.objects.get( name = dictionaryForTradedContract['name'], issuer__mainStrategy__betroom = betroom ),
                buyer = Player.objects.get( userBeingThisPlayer__username = dictionaryForTradedContract['buyer'], mainStrategy__betroom = betroom ),
                seller = Player.objects.get( userBeingThisPlayer__username = dictionaryForTradedContract['seller'], mainStrategy__betroom = betroom ),
                tradeDate = tradeDate,
                tradedPrice = dictionaryForTradedContract['tradedPrice']
            )
        tc.save()
    return 0

##################################################################################################
# add the liquidity tokens
def addLiquidityTokens(path,betroom):

    tokenObjects = getObjectFromFile( os.path.join( path, 'LiquidityTokens.txt') )
    checkDictionary( tokenObjects, ['liquidityTokens'] )
    tradedContractEntries = ['bid', 'contract', 'holder', 'offer']
    for i, v in enumerate( tokenObjects['liquidityTokens']): 
        checkDictionary( v, tradedContractEntries )
        
    # erase all existing traded contract
    LiquidityToken.objects.filter( bid__contract__issuer__mainStrategy__betroom = betroom ).delete()
            
    # create traded contract
    for i, dictionaryForToken in enumerate(tokenObjects['liquidityTokens']):
        contract = DigitalContract.objects.get( name = dictionaryForToken['contract'], issuer__mainStrategy__betroom = betroom )
        player = Player.objects.get( userBeingThisPlayer__username = dictionaryForToken['holder'], mainStrategy__betroom = betroom )
        bid = BidOnContract(
                contract = contract,
                player = player,
                price = dictionaryForToken['bid']
            )
        bid.save()
        offer = OfferOnContract(
                contract = contract,
                player = player,
                price = dictionaryForToken['offer']
                )
        offer.save()
        token = LiquidityToken(
                bid = bid,
                offer = offer,
            )
        token.save()
    return 0

##################################################################################################
#construct the base from the differents objects
def constructRoomFromFiles( unAdjustedPath, betroom ):

    # construct core objects
    pathAndAsset = addAssetsToBase(unAdjustedPath, betroom)
    adjustedPath = pathAndAsset[0]
    allStratNames = addStrategiesToBase( adjustedPath, betroom, pathAndAsset[1] )
    addPlayersToBase(adjustedPath,betroom, allStratNames )
    if betroom.roomType == 1:
        addContracts(adjustedPath,betroom)
        addTradedContracts(adjustedPath,betroom)
        addLiquidityTokens(adjustedPath,betroom)
        

    
    # construct non-core
    addBetroomInfoToBase(adjustedPath,betroom)
    addHistoryToBase(adjustedPath,betroom)

    print( betroom.name +' has been successfully loaded in the data base')





